﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Execution;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NUnitForVSTS;
using NUnitSide = NUnit.Core;
// We are using multiple namespaces with muliple classes with the same name, different meaning
// Therefore some alliasses to improve clarity
using TestTarget = NUnitTest.UnitTest.Targets;

namespace NUnitForVSTS.NUnitTest.UT
{
    /// <summary>
    ///This is a test class for NUnitTestAdapterTest and is intended
    ///to contain all NUnitTestAdapterTest Unit Tests
    ///</summary>
    [TestClass()]
    public class NUnitTestAdapterTest
    {
        /// <summary>
        ///A test for StopTestRun
        ///</summary>
        [TestMethod()]
        public void StopTestRunTest()
        {
            NUnitTestAdapter target = new NUnitTestAdapter();
            target.StopTestRun();
        }

        /// <summary>
        ///A test for AbortTestRun
        ///</summary>
        [TestMethod()]
        public void AbortTestRunTest()
        {
            NUnitTestAdapter target = new NUnitTestAdapter();
            target.AbortTestRun();
        }

        /// <summary>
        ///A test for Pause and Resume TestRun
        ///</summary>
        [TestMethod()]
        public void PauseAndResumeTestRunTest()
        {
            NUnitTestAdapter target = new NUnitTestAdapter();
            target.PauseTestRun();
            target.ResumeTestRun();
        }

        /// <summary>
        ///A test for Run
        ///</summary>
        [TestMethod()]
        public void RunTest()
        {
            Type testClassType = typeof(TestTarget.TestFixtureSetupCorrect);
            string testName = "HasBeenSetup";
            DummyTest test = CreateTestDummy(testClassType, testName);

            Assembly assembly = testClassType.Assembly;
            UnitTest testElement = new UnitTest(test, assembly.Location);
            testElement.Storage = assembly.Location;
            ITestElement[] testElements = new ITestElement[] { testElement };

            string actual = ExecuteAdapterTestRun(new NUnitTestAdapter(), assembly.Location, testElements);
            Assert.IsFalse(actual.Contains("Error"));
            Assert.IsTrue(actual.Contains("Outcome: Passed"));
            Assert.IsTrue(actual.Contains("HasBeenSetup"));
        }

        /// <summary>
        /// Runs the error test.
        /// </summary>
        [TestMethod()]
        public void RunErrorTest()
        {
            Type testClassType = typeof(TestTarget.TestFixtureSetupException);
            string testName = "NotBeenSetupShouldFail";
            DummyTest test = CreateTestDummy(testClassType, testName);

            Assembly assembly = testClassType.Assembly;
            UnitTest testElement = new UnitTest(test, assembly.Location);
            testElement.Storage = assembly.Location;
            ITestElement[] testElements = new ITestElement[] { testElement };

            string actual = ExecuteAdapterTestRun(new NUnitTestAdapter(), assembly.Location, testElements);
            Assert.IsFalse(actual.Contains("Error"));
            Assert.IsTrue(actual.Contains("Outcome: Failed"));
            Assert.IsTrue(actual.Contains("NotBeenSetupShouldFail"));
        }

        /// <summary>
        ///A test for Run
        ///</summary>
        [TestMethod()]
        public void RunOrderTest()
        {
            Type testClassType = typeof(TestTarget.TestFixtureOrderTwo);
            Assembly assembly = testClassType.Assembly;
            string[] testNames = { "TestOne", "TestTwo" };

            List<ITestElement> testElements = new List<ITestElement>();
            foreach (string testName in testNames)
            {
                DummyTest test = CreateTestDummy(testClassType, testName);

                UnitTest testElement = new UnitTest(test, assembly.Location);
                testElement.HostType = "NUnit";
                testElement.Storage = assembly.Location;
                testElements.Add(testElement);
            }

            string actual = ExecuteAdapterTestRun(new NUnitTestAdapter(), assembly.Location, testElements.ToArray());

            Assert.IsFalse(actual.Contains("Error"));
            Assert.IsTrue(actual.Contains("Outcome: Passed"));
            Assert.IsTrue(actual.Contains(testNames[0]));
            Assert.IsTrue(actual.Contains(testNames[1]));
        }

        private static DummyTest CreateTestDummy(Type testClassType, string testName)
        {
            MethodInfo testMethod = testClassType.GetMethod(testName);

            if (testMethod == null)
            {
                throw new MissingMethodException(testName);
            }

            DummyTest test = new DummyTest();
            test.TestName = new NUnitSide.TestName();
            test.TestName.FullName = testClassType.FullName + "." + testMethod.Name;
            test.TestName.Name = testMethod.Name;
            test.TestType = testClassType.FullName;
            return test;
        }

        /// <summary>
        /// Executes a testrun on the nunit test adapter
        /// </summary>
        /// <param name="testClassType">Type of the test class.</param>
        /// <param name="assembly">The assembly.</param>
        /// <param name="testElement">The test element.</param>
        /// <param name="testElements">The test elements.</param>
        /// <returns></returns>
        private static string ExecuteAdapterTestRun(NUnitTestAdapter target, string location, ITestElement[] testElements)
        {
            DummyResultSink result = new DummyResultSink();
            DummyRunConfig runConfig = new DummyRunConfig();
            runConfig.TestElements = testElements;

            DummyContextAccessor runContextVariables = new DummyContextAccessor();
            runContextVariables.AddStringValue("TestDeploymentDir", Path.GetDirectoryName(location));

            DummyRunContext runContext = new DummyRunContext();
            runContext.ResultSink = result;
            runContext.RunConfig = runConfig;
            runContext.RunContextVariables = runContextVariables;

            DummyTestContext testContext = new DummyTestContext();
            testContext.RunConfig = runConfig;
            testContext.ResultSink = result;
            testContext.RunContextVariables = runContextVariables;

            target.Initialize(runContext);

            string testTarget = location;

            foreach (ITestElement testElement in testElements)
            {
                target.Run(testElement, testContext);
            }

            target.PreTestRunFinished(runContext);
            target.Cleanup();

            string actual = result.Result.ToString();

            return actual;
        }


        /// <summary>
        ///A test for Cleanup
        ///</summary>
        [TestMethod()]
        public void CleanupEmptyTest()
        {
            NUnitTestAdapter target = new NUnitTestAdapter();
            DummyRunContext context = new DummyRunContext();
            context.RunContextVariables = new ContextBase(new string[] { "TestDeploymentDir" }, new string[] { "TestDeploymentDir" }, new string[] { "." });
            target.Initialize(context);
            target.Cleanup();
        }

        /// <summary>
        ///A test for NUnitTestAdapter Constructor
        ///</summary>
        [TestMethod()]
        public void NUnitTestAdapterConstructorTest()
        {
            NUnitTestAdapter target = new NUnitTestAdapter();
            string actual = target.ToString();
            Assert.IsNotNull(actual);
        }

        /// <summary>
        /// tests the adapters receive message.
        /// </summary>
        public void NUnitTestAdapterReceiveMessage()
        {
            NUnitTestAdapter target = new NUnitTestAdapter();
            target.ReceiveMessage(this);
        }

        /// <summary>
        ///A test for Run
        ///</summary>
        [TestMethod()]
        public void RunInterleavedTest()
        {
            Type[] testClassType = new Type[] {typeof(TestTarget.TestFixtureOrderOne), 
                                                typeof(TestTarget.TestFixtureOrderTwo)};
            Assembly assembly = testClassType[0].Assembly;
            string[] testNames = { "TestOne", "TestTwo" };

            List<ITestElement> testElements = new List<ITestElement>();
            foreach (Type type in testClassType)
            {
                foreach (string testName in testNames)
                {
                    DummyTest test = CreateTestDummy(type, testName);

                    UnitTest testElement = new UnitTest(test, assembly.Location);
                    testElement.HostType = "NUnit";
                    testElements.Add(testElement);
                    testElement.Storage = assembly.Location;
                }
            }

            NUnitTestAdapter adapter = new NUnitTestAdapter();
            string actual = ExecuteAdapterTestRun(adapter, assembly.Location, testElements.ToArray());

            Assert.IsFalse(actual.Contains("Error"), actual);
            Assert.IsTrue(actual.Contains("Outcome: Passed"), actual);
            Assert.IsFalse(actual.Contains("Outcome: Failed"), actual);
            Assert.IsTrue(actual.Contains(testNames[0]), actual);
            Assert.IsTrue(actual.Contains(testNames[1]), actual);
        }

        /// <summary>
        // A terst to see if the app.config is loaded correctly
        /// </summary>
        [TestMethod()]
        public void RunAppConfigTest()
        {
            Type testClassType = typeof(TestTarget.TestAppConfig);
            string configFile = testClassType.Assembly.Location + ".config";
            string oldFile = Guid.NewGuid().ToString();
            if (File.Exists(configFile))
            {
                File.Move(configFile, oldFile);
            }
            File.WriteAllText(testClassType.Assembly.Location + ".config",
                                @"<?xml version='1.0' encoding='utf-8' ?>
                                <configuration>
                                    <appSettings>
                                        <add key='importantSetting' value='Important value'/>
                                    </appSettings>
                                </configuration>");
            Assembly assembly = testClassType.Assembly;
            string[] testNames = { "TestApplicationconfigLoad" };

            List<ITestElement> testElements = new List<ITestElement>();
            foreach (string testName in testNames)
            {
                DummyTest test = CreateTestDummy(testClassType, testName);

                UnitTest testElement = new UnitTest(test, assembly.Location);
                testElement.HostType = "NUnit";
                testElement.Storage = testClassType.Assembly.Location;
                testElements.Add(testElement);
            }

            NUnitTestAdapter adapter = new NUnitTestAdapter();
            string actual = ExecuteAdapterTestRun(adapter, assembly.Location, testElements.ToArray());

            Assert.IsFalse(actual.Contains("Error"), actual);
            Assert.IsTrue(actual.Contains("Outcome: Passed"), actual);
            Assert.IsFalse(actual.Contains("Outcome: Failed"), actual);
            Assert.IsTrue(actual.Contains(testNames[0]), actual);

            File.Delete(configFile);
            if (File.Exists(oldFile))
            {
                File.Move(oldFile, configFile);
            }
        }

        /// <summary>
        ///A test for Run
        ///Test is removed between discovery and run
        ///</summary>
        [TestMethod()]
        public void RunRemovedTest()
        {
            string testName = "RunRemovedTest";

            List<ITestElement> testElements = new List<ITestElement>();
             DummyTest test = CreateTestDummy(GetType(), testName);

                UnitTest testElement = new UnitTest(test, Path.GetTempPath());
                testElement.HostType = "NUnit";
                testElements.Add(testElement);
                testElement.Storage = Path.GetTempFileName();
                File.Create(testElement.Storage);

                string actual = ExecuteAdapterTestRun(new NUnitTestAdapter(), testElement.Storage, new ITestElement[] { testElement });

                Assert.IsTrue(actual.Contains("NotRunnable"), actual);
            Assert.IsFalse(actual.Contains("Outcome: Passed"));
            Assert.IsFalse(actual.Contains("Outcome: Failed"));
            Assert.IsFalse(actual.Contains("Outcome: Error"));
            Assert.IsTrue(actual.Contains(testName));
        }

        /// <summary>
        /// Runs the error test.
        /// Ignored because we do not support this.
        /// </summary>
        [TestMethod()]
        public void RunTestDomainUnload()
        {
            Type testClassType = typeof(TestTarget.TestDomainUnload);
            string testName = "UnloadTestShouldError";
            DummyTest test = CreateTestDummy(testClassType, testName);

            Assembly assembly = testClassType.Assembly;
            UnitTest testElement = new UnitTest(test, assembly.Location);
            testElement.Storage = assembly.Location;
            ITestElement[] testElements = new ITestElement[] { testElement };

            string actual = ExecuteAdapterTestRun(new NUnitTestAdapter(), assembly.Location, testElements);
            Assert.IsTrue(actual.Contains("Error"));
            Assert.IsTrue(actual.Contains(testName));
        }

        [TestMethod()]
        public void TestAdapterInitalizeLifeTimeTest()
        {
            NUnitTestAdapter adapter = new NUnitTestAdapter();
            Assert.IsNull(adapter.InitializeLifetimeService(), "Should have infinite lifetime");
        }

        /// <summary>
        /// Tests the adapter recieve message test.
        /// Not realy usefull i.m.h.o. But it should not crash
        /// </summary>
        [TestMethod()]
        public void TestAdapterRecieveMessageTest()
        {
            NUnitTestAdapter adapter = new NUnitTestAdapter();
            adapter.ReceiveMessage(null);
            adapter.ReceiveMessage(new object());
        }

        [TestMethod()]
        public void ExecuteWrongTestType()
        {
            string testName = "RunRemovedTest";

            List<ITestElement> testElements = new List<ITestElement>();
            DummyTest test = CreateTestDummy(GetType(), testName);

            ITestElement testElement = new OtherTest();
            testElements.Add(testElement);
            testElement.Storage = Path.GetTempFileName();
            File.Create(testElement.Storage);

            string actual = ExecuteAdapterTestRun(new NUnitTestAdapter(), testElement.Storage, new ITestElement[] { testElement });

            Assert.IsTrue(actual.Contains("Wrong Test Type"), actual);
        }
        private class OtherTest : ITestElement
        {
            #region ITestElement Members

            public bool AbortRunOnAgentFailure
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public string Adapter
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public System.Collections.Specialized.StringDictionary AgentAttributes
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public void AssignIsRunnable(bool runnable, string errorMessage)
            {
                throw new NotImplementedException();
            }

            public bool CanBeAggregated
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public TestCategoryId CategoryId
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public string ControllerPlugin
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public ITestElement Copy
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public bool CreatedByUI
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public string CssIteration
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public string CssProjectStructure
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public DeploymentItemCollection DeploymentItems
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public string Description
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public bool Enabled
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public string ErrorMessageForNonRunnable
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public TestExecId ExecutionId
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public System.ComponentModel.PropertyDescriptorCollection Groups
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public string HumanReadableId
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public TestId Id
            {
                get
                {
                    return new TestId();
                }
            }

            public bool IsAutomated
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public bool IsGroupable
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public bool IsModified
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public bool IsRunOnRestart
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public bool IsRunnable
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public Link Link
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public string Name
            {
                get;
                set;
            }

            public string Owner
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public TestExecId ParentExecId
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public int Priority
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public ProjectData ProjectData
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public string ProjectName
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public string ProjectRelativePath
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public System.Collections.Hashtable Properties
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public bool ReadOnly
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public string SolutionName
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public string SourceFileName
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public string Storage
            {
                get;
                set;
            }

            public TestType TestType
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            public int Timeout
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public object UserData
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }

            public System.Collections.IList WorkItemIds
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            #endregion

            #region ICloneable Members

            public object Clone()
            {
                throw new NotImplementedException();
            }

            #endregion

            #region IVerifiable Members

            public bool IsValid()
            {
                throw new NotImplementedException();
            }

            #endregion

            #region IVisiblePropertyProvider Members

            public System.ComponentModel.PropertyDescriptorCollection VisibleProperties
            {
                get
                {
                    throw new NotImplementedException();
                }
            }

            #endregion
        }
    }
}
